#ifndef __FAMILY_HPP__
#define __FAMILY_HPP__

#include <unordered_map>
#include <chrono>
#include <algorithm>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <list>
#include <functional>

#include "metrics/utility/Date.hpp"
#include "metrics/utility/Hash.hpp"
#include "metrics/Collectable.hpp"
#include "metrics/ClientMetric.hpp"
#include "metrics/MetricFamily.hpp"

namespace metrics
{
    template <typename T>
    class Family : public Collectable
    {
    private:
        const std::string _name;
        const std::string _help;
        const std::map<std::string, std::string> _family_labels;

        std::unordered_map<std::size_t, std::shared_ptr<T>> _metrics;
        std::unordered_map<std::size_t, std::map<std::string, std::string>> _labels;
        std::unordered_map<std::shared_ptr<T>, std::size_t> _labels_reverse_lookup;

        mutable std::mutex _mutex;

        std::list<std::function<void()>> _observers;

    private:
        std::shared_ptr<T> add(const std::map<std::string, std::string> &labels, std::shared_ptr<T> object);
        void update() const;
        /**
         * @brief 收集metrics
         * 
         * @param hash 哈希值
         * @param metric metric
         * @return ClientMetric 结果 
         */
        ClientMetric collect_metric(std::size_t hash, T *metric) const;

    public:
        Family() = delete;
        ~Family() = default;
        Family(const std::string &name, const std::string &help, const std::map<std::string, std::string> &labels);

        /**
         * @brief 添加回调, 在collect函数中自动调用
         * 
         * @param observer 回调函数
         */
        void observe(std::function<void()> &&observer);
        /**
         * @brief 添加metric
         * 
         * @tparam Args 
         * @param labels 标签
         * @param args 构造参数
         * @return std::shared_ptr<T> metric
         */
        template <typename... Args>
        std::shared_ptr<T> add(const std::map<std::string, std::string> &labels, Args &&...args);
        /**
         * @brief 移除metric
         * 
         */
        void remove(std::shared_ptr<T>);
        /**
         * @brief 返回family name
         * 
         * @return const std::string& name
         */
        const std::string &name() const;
        /**
         * @brief 返回family help
         * 
         * @return const std::string& help
         */
        const std::string &help() const;
        /**
         * @brief 返回family labels
         * 
         * @return const std::map<std::string, std::string> labels
         */
        const std::map<std::string, std::string> &labels() const;
        /**
         * @brief 收集metric目前的值
         * 
         * @return std::vector<MetricFamily> metrics
         */
        virtual std::vector<MetricFamily> collect() const;
    };

    template <typename T>
    Family<T>::Family(const std::string &name, const std::string &help, const std::map<std::string, std::string> &labels)
        : _name(name), _help(help), _family_labels(labels) {}

    template <typename T>
    void Family<T>::observe(std::function<void()> &&observer)
    {
        _observers.push_back(std::forward<std::function<void()>>(observer));
    }

    template <typename T>
    void Family<T>::update() const
    {
        for (auto &func : _observers)
        {
            func();
        }
    }

    template <typename T>
    template <typename... Args>
    std::shared_ptr<T> Family<T>::add(const std::map<std::string, std::string> &labels, Args &&...args)
    {
        return add(labels, std::make_shared<T>(args...));
    }

    template <typename T>
    std::shared_ptr<T> Family<T>::add(const std::map<std::string, std::string> &labels, std::shared_ptr<T> object)
    {
        const auto hash_value = hash_labels(labels);
        std::lock_guard<std::mutex> lock{_mutex};
        auto metrics_iter = _metrics.find(hash_value);
        if (metrics_iter != _metrics.end())
        {
            return metrics_iter->second;
        }
        auto metric = _metrics.insert(std::make_pair(hash_value, object));
        _labels.insert({hash_value, labels});
        _labels_reverse_lookup.insert({metric.first->second, hash_value});
        return metric.first->second;
    }

    template <typename T>
    void Family<T>::remove(std::shared_ptr<T> metric)
    {
        std::lock_guard<std::mutex> lock{_mutex};
        if (_labels_reverse_lookup.count(metric) == 0)
        {
            return;
        }

        const auto hash = _labels_reverse_lookup.at(metric);
        _metrics.erase(hash);
        _labels.erase(hash);
        _labels_reverse_lookup.erase(metric);
    }

    template <typename T>
    const std::string &Family<T>::name() const
    {
        return _name;
    }

    template <typename T>
    const std::string &Family<T>::help() const
    {
        return _help;
    }

    template <typename T>
    const std::map<std::string, std::string> &Family<T>::labels() const
    {
        return _family_labels;
    }

    template <typename T>
    std::vector<MetricFamily> Family<T>::collect() const
    {
        //先执行观察者
        update();
        std::lock_guard<std::mutex> lock{_mutex};

        MetricFamily family;
        family.name = _name;
        family.help = _help;
        family.labels = _family_labels;
        family.type = T::METRIC_TYPE;
        if (!_metrics.empty())
        {
            family.metric.reserve(_metrics.size());
            for (const auto &m : _metrics)
            {
                family.metric.push_back(std::move(collect_metric(m.first, m.second.get())));
            }
        }

        return {family};
    }

    template <typename T>
    ClientMetric Family<T>::collect_metric(std::size_t hash, T *metric) const
    {
        auto collected = metric->collect();
        collected.labels = _labels.at(hash);
        return collected;
    }

} // namespace metrics

#endif // __FAMILY_HPP__